package com.atguigu.gmall.wms.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.common.exception.OrderException;
import com.atguigu.gmall.wms.vo.SkuLockVo;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.gmall.common.bean.PageResultVo;
import com.atguigu.gmall.common.bean.PageParamVo;

import com.atguigu.gmall.wms.mapper.WareSkuMapper;
import com.atguigu.gmall.wms.entity.WareSkuEntity;
import com.atguigu.gmall.wms.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuMapper, WareSkuEntity> implements WareSkuService {

    @Autowired
    private WareSkuMapper wareSkuMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    private static final String KEY_PREFIX = "store:info:";
    private static final String LOCK_PREFIX = "store:lock:";

    @Override
    public PageResultVo queryPage(PageParamVo paramVo) {
        IPage<WareSkuEntity> page = this.page(
                paramVo.getPage(),
                new QueryWrapper<WareSkuEntity>()
        );

        return new PageResultVo(page);
    }

    @Transactional
    @Override
    public List<SkuLockVo> checkAndLock(List<SkuLockVo> skuLockVos,String orderToken) {
        // 判空
        if (CollectionUtils.isEmpty(skuLockVos)){
            throw  new OrderException("要购买的商品不能为空");
        }
        //遍历所有商品: 验库存并锁库存
        skuLockVos.forEach(skuLockVo -> {
            //每一个商品验库存并锁库存
            this.checkLock(skuLockVo);
        });
        // 如果有一个商品锁定失败了,所有已经成功锁定的商品要解库存
        List<SkuLockVo> successLockVo = skuLockVos.stream().filter(SkuLockVo::getLock).collect(Collectors.toList());
        List<SkuLockVo> errorLockVo = skuLockVos.stream().filter(skuLockVo ->
                !skuLockVo.getLock()).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(errorLockVo)){
            successLockVo.forEach(skuLockVo -> {
                this.wareSkuMapper.unlockStock(skuLockVo.getWareSkuId(),skuLockVo.getCount());
            });
            // 返回锁定信息: 哪些锁定成功,哪些锁定失败
            return skuLockVos;
        }

        // 把库存的锁定信息保存到redis中,以方便将来解锁库存
        this.redisTemplate.opsForValue().set(KEY_PREFIX + orderToken, JSON.toJSONString(skuLockVos));
        return null; //如果都锁定成功,不需要展示锁定情况
    }


    private void checkLock(SkuLockVo skuLockVo){
        RLock fairLock = this.redissonClient.getFairLock(LOCK_PREFIX + skuLockVo.getSkuId());
        fairLock.lock();
        try {
            //验库存
            List<WareSkuEntity> wareSkuEntities = this.wareSkuMapper.checkStock(skuLockVo.getSkuId(),skuLockVo.getCount());
            if (CollectionUtils.isEmpty(wareSkuEntities)){
                skuLockVo.setLock(false); // 库存不足,锁定失败
                fairLock.unlock(); //程序返回之前,一定要释放锁
                return;
            }
            // 锁库存. 一般会根据运输距离,就近调配.这里就锁定第一个仓库的库存
            if (this.wareSkuMapper.lockStock(wareSkuEntities.get(0).getId(),skuLockVo.getCount()) == 1){
                skuLockVo.setLock(true); //锁定成功
                // 一旦锁定成功,需要记录锁定库存的id
                skuLockVo.setWareSkuId(wareSkuEntities.get(0).getId());
            }
        } finally {
            fairLock.unlock();
        }
    }

}